﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Collections;
using System.IO;
using System.Threading;

namespace ReadShapeFile_test
{
    public partial class ReadSHPFileForm : Form
    {
        List<Polygon> polygons;
        List<Polyline> polylines;
        List<Point> points;
        Pen bPen, rPen, blPen;
        SolidBrush yBrush, bBrush;
        Font font;
        Table table;
        OpenFileDialog openFileDialog1, openFileDialog2;
        int ShapeType;
        double xmin, ymin, xmax, ymax;
        double n1, n2, n;
        double originWidth;

        private void ReadSHPFileForm_Load(object sender, EventArgs e)
        {
            polygons = new List<Polygon>();
            polylines = new List<Polyline>();
            points = new List<Point>();
            bPen = new Pen(Color.Black, 1);
            rPen = new Pen(Color.Red, 1);
            blPen = new Pen(Color.Blue, 1);
            yBrush = new SolidBrush(Color.Yellow);
            bBrush = new SolidBrush(Color.Black);
            font = new Font("宋体", 8, FontStyle.Bold);
            table = new Table();
            originWidth = this.DrawingBoard.Width;
            xmin = ymin = xmax = ymax = 0;
            n = 1;
        }

        private void OpenSHPFileButton_Click(object sender, EventArgs e)
        {
            openFileDialog1 = new OpenFileDialog();
            openFileDialog1.Filter = "shapefiles(*.shp)|*.shp|All files(*.*)|*.*";
            openFileDialog1.FilterIndex = 1;
            if (openFileDialog1.ShowDialog() == DialogResult.OK)
            {
                ReadSHPFile(openFileDialog1);
                char[] path = openFileDialog1.FileName.ToCharArray();
                if (path.Length != 0)
                {
                    path[path.Length - 1] = 'f';
                    path[path.Length - 2] = 'b';
                    path[path.Length - 3] = 'd';
                    openFileDialog1.FileName = new string(path);
                    ReadDBFFile(openFileDialog1);
                }
                DrawingBoard.Refresh();
            }
        }

        private void OpenDBFFileButton_Click(object sender, EventArgs e)
        {
            openFileDialog2 = new OpenFileDialog();
            openFileDialog2.Filter = "dbffiles(*.dbf)|*.dbf|All files(*.*)|*.*";
            openFileDialog2.FilterIndex = 1;
            if (openFileDialog2.ShowDialog() == DialogResult.OK)
            {
                ReadDBFFile(openFileDialog2);
            }
        }

        private void RefreshButton_Click(object sender, EventArgs e)
        {
            DrawingBoard.Controls.Clear();
            DrawingBoard.Refresh();
        }

        private void MagnifyButton_Click(object sender, EventArgs e)
        {
            if (openFileDialog1 != null)
            {
                n += 0.1;
                double width = xmax - xmin;
                double height = ymax - ymin;
                int w = (int)(originWidth * n);
                int h = (int)(originWidth / width * height * n);
                DrawingBoard.Size = new Size(w, h);
                DrawingBoard.Refresh();
            }
        }

        private void ShrinkButton_Click(object sender, EventArgs e)
        {
            if (openFileDialog1 != null)
            {
                n -= 0.1;
                double width = xmax - xmin;
                double height = ymax - ymin;
                int w = (int)(originWidth * n);
                int h = (int)(originWidth / width * height * n);
                DrawingBoard.Size = new Size(w, h);
                DrawingBoard.Refresh();
            }
        }

        private void DarwingBoard_Paint(object sender, PaintEventArgs e)
        {
            ZoomLabel.Text = "X" + n.ToString();
            if (polygons != null) DrawPolygons(e);
            if (polylines != null) DrawPolylines(e);
            if (points != null) DrawPoints(e);
        }
        
        private void ReadDBFFile(OpenFileDialog openFileDialog)
        {
            table.dt.Columns.Clear();
            table.dt.Rows.Clear();
            table.columnsName.Clear();
            table.columnsLength.Clear();
            BinaryReader br = new BinaryReader(openFileDialog.OpenFile());
            _ = br.ReadByte();//当前版本信息
            _ = br.ReadBytes(3);//最近更新日期
            table.rowCount = br.ReadInt32();//文件中的记录条数
            table.columnsCount = (br.ReadInt16()-33)/32;//文件头中的字节数
            _ = br.ReadInt16();//一条记录中的字节长度
            _ = br.ReadBytes(20);//系统保留
            for(int i=0; i< table.columnsCount; i++)
            {
                string name = System.Text.Encoding.Default.GetString(br.ReadBytes(10));
                table.dt.Columns.Add(new DataColumn(name, typeof(string)));
                table.columnsName.Add(name);
                _ = br.ReadBytes(6);
                table.columnsLength.Add(br.ReadByte());
                _ = br.ReadBytes(15);
            }
            _ = br.ReadBytes(1);//终止符0x0D
            for (int i = 0; i < table.rowCount; i++)
            {
                _ = br.ReadByte();//占位符
                DataRow dr;
                dr = table.dt.NewRow();
                for (int j = 0; j < table.columnsCount; j++)
                {
                    string temp = System.Text.Encoding.Default.GetString(br.ReadBytes(table.columnsLength[j]));
                    if (j == 0) table.ID.Add(temp);
                    dr[(string)table.columnsName[j]] = temp;
                }
                table.dt.Rows.Add(dr);
            }
            ShowBDFFileView.DataSource = table.dt;
        }

        private void ReadSHPFile(OpenFileDialog openFileDialog)
        {
            //读取主文件头
            BinaryReader br = new BinaryReader(openFileDialog.OpenFile());
            _ = br.ReadBytes(24);//文件编号及未被使用的字段
            _ = br.ReadInt32();//文件长度
            _ = br.ReadInt32();//版本
            ShapeType = br.ReadInt32();
            double temp = br.ReadDouble();
            if (xmin == 0 || temp < xmin)
                xmin = temp;
            temp = -br.ReadDouble();
            if (ymax == 0 || temp > ymax)
                ymax = temp;
            temp = br.ReadDouble();
            if (xmax == 0 || temp > xmax)
                xmax = temp;
            temp = -br.ReadDouble();
            if (ymin == 0 || temp < ymin)
                ymin = temp;
            _ = br.ReadBytes(32);//Z、M的范围
            double width = xmax - xmin;
            double height = ymax - ymin;
            n1 = (float)(originWidth * 0.9 / width);
            n2 = (float)(n1 / width * height);
            //读取主文件记录内容
            switch (ShapeType)
            {
                case 1:
                    points.Clear();
                    while (br.PeekChar() != -1)
                    {
                        Point point = new Point();
                        _ = br.ReadInt32();//记录编号
                        _ = br.ReadInt32();//记录内容长度
                        _ = br.ReadInt32();//记录内容头的图形类型编号
                        point.X = 10 + (br.ReadDouble() - xmin) * n1;
                        point.Y = 10 + (-br.ReadDouble() - ymin) * n2;
                        points.Add(point);
                    }
                    //StreamWriter sw = new StreamWriter("point.txt");
                    //foreach (Point p in points)
                    //{
                    //    sw.WriteLine("{0},{1},{2} ", p.X, -1 * p.Y, 0);
                    //}
                    //sw.Close();
                    break;
                case 3:
                    polylines.Clear();
                    while (br.PeekChar() != -1)
                    {
                        _ = br.ReadInt32();//记录编号
                        _ = br.ReadInt32();//记录内容长度
                        _ = br.ReadInt32();//记录内容头的图形类型编号
                        Polyline polyline = new Polyline();
                        polyline.Box[0] = 10 + (br.ReadDouble() - xmin) * n1;
                        polyline.Box[3] = 10 + (-br.ReadDouble() - ymin) * n2;
                        polyline.Box[2] = 10 + (br.ReadDouble() - xmin) * n1;
                        polyline.Box[1] = 10 + (-br.ReadDouble() - ymin) * n2;
                        polyline.NumParts = br.ReadInt32();
                        polyline.NumPoints = br.ReadInt32();
                        for (int i = 0; i < polyline.NumParts; i++)
                        {
                            polyline.Parts.Add(br.ReadInt32());
                        }
                        for (int j = 0; j < polyline.NumPoints; j++)
                        {
                            Point point = new Point();
                            point.X = 10 + (br.ReadDouble() - xmin) * n1;
                            point.Y = 10 + (-br.ReadDouble() - ymin) * n2;
                            polyline.Points.Add(point);
                        }
                        polylines.Add(polyline);
                    }
                    break;
                case 5:
                    polygons.Clear();
                    while (br.PeekChar() != -1)
                    {
                        _ = br.ReadInt32();//记录编号
                        _ = br.ReadInt32();//记录内容长度
                        _ = br.ReadInt32();//记录内容头的图形类型编号
                        Polygon polygon = new Polygon();
                        polygon.Box[0] = 10 + (br.ReadDouble() - xmin) * n1;
                        polygon.Box[3] = 10 + (-br.ReadDouble() - ymin) * n2;
                        polygon.Box[2] = 10 + (br.ReadDouble() - xmin) * n1;
                        polygon.Box[1] = 10 + (-br.ReadDouble() - ymin) * n2;
                        polygon.NumParts = br.ReadInt32();
                        polygon.NumPoints = br.ReadInt32();
                        for (int i = 0; i < polygon.NumParts; i++)
                        {
                            polygon.Parts.Add(br.ReadInt32());
                        }
                        for (int j = 0; j < polygon.NumPoints; j++)
                        {
                            Point point = new Point();
                            point.X = 10 + (br.ReadDouble() - xmin) * n1;
                            point.Y = 10 + (-br.ReadDouble() - ymin) * n2;
                            polygon.Points.Add(point);
                        }
                        polygons.Add(polygon);
                    }
                    break;
            }
        }

        private void DrawPolygons(PaintEventArgs e)
        {
            PointF[] point;
            int count = 0;
            foreach (Polygon p in polygons)
            {
                for (int i = 0; i < p.NumParts; i++)
                {
                    int startpoint;
                    int endpoint;
                    if (i == p.NumParts - 1)
                    {
                        startpoint = p.Parts[i];
                        endpoint = p.NumPoints;
                    }
                    else
                    {
                        startpoint = p.Parts[i];
                        endpoint = p.Parts[i + 1];
                    }
                    point = new PointF[endpoint - startpoint];
                    for (int k = 0, j = startpoint; j < endpoint; j++, k++)
                    {
                        point[k].X = (float)(p.Points[j].X * n);
                        point[k].Y = (float)(p.Points[j].Y * n);
                    }
                    e.Graphics.DrawPolygon(bPen, point);
                    e.Graphics.FillPolygon(yBrush, point);
                }
                if (table.ID.Count != 0)
                {
                    float xx = (float)((p.Box[0] + p.Box[2]) * n / 2);
                    float yy = (float)((p.Box[1] + p.Box[3]) * n / 2);
                    e.Graphics.DrawString("ID:" + Convert.ToInt32(table.ID[count]), font, bBrush, xx, yy);
                    ++count;
                }
            }
        }

        private void DrawPolylines(PaintEventArgs e)
        {
            PointF[] point;
            //int count = 0;
            foreach (Polyline p in polylines)
            {
                for (int i = 0; i < p.NumParts; i++)
                {
                    int startpoint;
                    int endpoint;
                    if (i == p.NumParts - 1)
                    {
                        startpoint = p.Parts[i];
                        endpoint = p.NumPoints;
                    }
                    else
                    {
                        startpoint = p.Parts[i];
                        endpoint = p.Parts[i + 1];
                    }
                    point = new PointF[endpoint - startpoint];
                    for (int k = 0, j = startpoint; j < endpoint; j++, k++)
                    {
                        point[k].X = (float)(p.Points[j].X * n);
                        point[k].Y = (float)(p.Points[j].Y * n);
                    }
                    e.Graphics.DrawLines(rPen, point);
                }
                //if (table.ID.Count != 0)
                //{
                //    int xx = (int)(10 + (p.Box[0] + p.Box[2] - 2 * xmin) * n1) / 2;
                //    int yy = (int)(10 + (p.Box[1] + p.Box[3] - 2 * ymin) * n2) / 2;
                //    e.Graphics.DrawString("ID:" + Convert.ToInt32(table.ID[count]), font, bBrush, xx, yy);
                //    ++count;
                //}
            }
        }

        private void DrawPoints(PaintEventArgs e)
        {
            //int count = 0;
            foreach (Point p in points)
            {
                float xx = (float)(p.X * n);
                float yy = (float)(p.Y * n);
                e.Graphics.DrawEllipse(blPen, xx, yy, 1f, 1f);
                //e.Graphics.DrawString("ID:" + Convert.ToInt32(table.ID[count]), font, bBrush, pp.X, pp.Y);
                //++count;
            }
        }

        public ReadSHPFileForm()
        {
            InitializeComponent();
        }
    }
}
